#include "ExMainHUD.h"
#include <QMessageBox>
#include <QDebug>
#include "qevent.h"
#include <experimental/resumable>
#include <filesystem>
#include "ExFunLib.h"

using namespace std::experimental;
//using namespace DirectX;
//using namespace std::filesystem;


#ifndef HR
#define HR(x)												\
	{															\
		HRESULT hr = (x);										\
		if(FAILED(hr))											\
		{														\
			QMessageBox::critical(this, "critical", QString("file[%1]line[%2]").arg(__FILEW__).arg(__LINE__));\
		}														\
	}
#endif

// 安全COM组件释放宏
#define SAFE_RELEASE(p) { if ((p)) { (p)->Release(); (p) = nullptr; } }

ExMainHUD::ExMainHUD(QWidget* parent)
	: QWidget(parent),
	m_pD3DDevice(nullptr),
	m_pD3DDeviceContext(nullptr),
	m_pD2DFactory(nullptr),
	m_pD2DDevice(nullptr),
	m_pD2DDeviceContext(nullptr),
	m_pWICFactory(nullptr),
	m_pDWriteFactory(nullptr),
	m_pSwapChain(nullptr),
	m_pD2DTargetBimtap(nullptr),
	m_pBrush(nullptr),
	m_pTextFormat(nullptr),
	m_pBitmap(nullptr),
	m_pEffectTest(nullptr)
{
	ui.setupUi(this);

	//设置窗口属性，关键步骤，否则D3D绘制出问题
	ui.DirectXWidget->setAttribute(Qt::WA_PaintOnScreen, true);
	ui.DirectXWidget->setAttribute(Qt::WA_NativeWindow, true);

	m_afData = 0.f;

	m_ClientWidth = ui.DirectXWidget->width();
	m_ClientHeight = ui.DirectXWidget->height();

	//2D图形窗口初始化
	if (!InitDirect2D())
		return;

	//3D图形窗口初始化
	if (!InitDirect3D())
		return;

	//重置游戏时间管理器
	m_Timer.Reset();


	//多线程模式开启渲染流, 保持帧率为60
	RenderThread = new ExRenderThread(this);
	connect(RenderThread, SIGNAL(RenderRun()), this, SLOT(UpdateRender()));
	RenderThread->start();

}

bool ExMainHUD::InitDirect2D()
{
	// 创建D2D工厂
	HR(D2D1CreateFactory(
		D2D1_FACTORY_TYPE_SINGLE_THREADED,
		__uuidof(ID2D1Factory1),
		reinterpret_cast<void**>(m_pD2DFactory.GetAddressOf())));
	// 创建 WIC 工厂.
	HR(CoCreateInstance(
		CLSID_WICImagingFactory2,
		nullptr,
		CLSCTX_INPROC_SERVER,
		IID_PPV_ARGS(m_pWICFactory.GetAddressOf())));
	// 创建 DirectWrite 工厂.
	HR(DWriteCreateFactory(
		DWRITE_FACTORY_TYPE_SHARED,
		__uuidof(m_pDWriteFactory),
		reinterpret_cast<IUnknown**>(m_pDWriteFactory.GetAddressOf())));
	return true;
}

bool ExMainHUD::InitDirect3D()
{
	// DXGI 工厂
	ComPtr<IDXGIFactory2> pDxgiFactory(nullptr);
	// DXGI 设备
	ComPtr<IDXGIDevice1> pDxgiDevice(nullptr);

	HRESULT hr = S_OK;

	// 创建 D3D11设备与设备上下文 
	if (SUCCEEDED(hr))
	{
		// D3D11 创建flag 
		// 一定要有D3D11_CREATE_DEVICE_BGRA_SUPPORT，否则创建D2D设备上下文会失败
		UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#ifdef _DEBUG
		// Debug状态 有D3D DebugLayer就可以取消注释
		creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
		D3D_FEATURE_LEVEL featureLevels[] = {
			D3D_FEATURE_LEVEL_11_1,
			D3D_FEATURE_LEVEL_11_0,
			D3D_FEATURE_LEVEL_10_1,
			D3D_FEATURE_LEVEL_10_0,
			D3D_FEATURE_LEVEL_9_3,
			D3D_FEATURE_LEVEL_9_2,
			D3D_FEATURE_LEVEL_9_1
		};
		// 创建设备
		hr = D3D11CreateDevice(
			nullptr,					// 设为空指针选择默认设备
			D3D_DRIVER_TYPE_HARDWARE,	// 强行指定硬件渲染
			nullptr,					// 强行指定WARP渲染 D3D_DRIVER_TYPE_WARP 没有软件接口
			creationFlags,				// 创建flag
			featureLevels,				// 欲使用的特性等级列表
			ARRAYSIZE(featureLevels),	// 特性等级列表长度
			D3D11_SDK_VERSION,			// SDK 版本
			m_pD3DDevice.GetAddressOf(),				// 返回的D3D11设备指针
			&m_featureLevel,			// 返回的特性等级
			m_pD3DDeviceContext.GetAddressOf());		// 返回的D3D11设备上下文指针
	}

	// 创建 IDXGIDevice
	if (SUCCEEDED(hr))
		hr = m_pD3DDevice->QueryInterface(IID_PPV_ARGS(pDxgiDevice.GetAddressOf()));
	// 创建D2D设备
	if (SUCCEEDED(hr))
		hr = m_pD2DFactory->CreateDevice(pDxgiDevice.Get(), m_pD2DDevice.GetAddressOf());
	// 创建D2D设备上下文
	if (SUCCEEDED(hr))
		hr = m_pD2DDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, m_pD2DDeviceContext.GetAddressOf());

	pDxgiDevice.Reset();
	pDxgiFactory.Reset();

	hr = m_pDWriteFactory->CreateTextFormat(
		L"Sitka Text",
		NULL,
		DWRITE_FONT_WEIGHT_NORMAL,
		DWRITE_FONT_STYLE_NORMAL,
		DWRITE_FONT_STRETCH_NORMAL,
		68.0,
		L"en-us",
		m_pTextFormat.GetAddressOf());

	hr = m_pD2DDeviceContext->CreateSolidColorBrush(
		D2D1::ColorF(D2D1::ColorF::DarkBlue), m_pBrush.GetAddressOf());

	hr = LoadBitmapFromFile(m_pD2DDeviceContext.Get(), m_pWICFactory.Get(), L"effect.png", 0, 0, m_pBitmap.GetAddressOf());

	hr = m_pD2DDeviceContext->CreateEffect(CLSID_D2D1DiscreteTransfer, m_pEffectTest.GetAddressOf());

	m_pEffectTest->SetInput(0, m_pBitmap.Get());

	return true;
}

void ExMainHUD::OnResize()
{
	// DXGI 适配器
	ComPtr<IDXGIAdapter> pDxgiAdapter(nullptr);
	// DXGI 工厂
	ComPtr<IDXGIFactory2> pDxgiFactory(nullptr);
	// DXGI Surface 后台缓冲
	ComPtr<IDXGISurface> pDxgiBackBuffer(nullptr);
	// DXGI 设备
	ComPtr<IDXGIDevice1> pDxgiDevice(nullptr);

	HRESULT hr = S_OK;

	// 清除之前窗口的呈现器相关设备
	m_pD2DDeviceContext->SetTarget(nullptr);
	m_pD2DTargetBimtap.Reset();
	m_pD3DDeviceContext->Flush();

	if (m_pSwapChain != nullptr)
	{
		// 如果交换链已经创建，则重设缓冲区
		hr = m_pSwapChain->ResizeBuffers(
			2, // Double-buffered swap chain.
			m_ClientWidth,
			m_ClientHeight,
			DXGI_FORMAT_B8G8R8A8_UNORM,
			0);

		assert(hr == S_OK);
	}
	else
	{
		// 否则用已存在的D3D设备创建一个新的交换链
		DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };
		swapChainDesc.Width = m_ClientWidth;
		swapChainDesc.Height = m_ClientHeight;
		swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
		swapChainDesc.Stereo = false;
		swapChainDesc.SampleDesc.Count = 1;
		swapChainDesc.SampleDesc.Quality = 0;
		swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		swapChainDesc.BufferCount = 2;
		swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
		swapChainDesc.Flags = 0;
		swapChainDesc.Scaling = DXGI_SCALING_NONE;
		swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;

		// 获取 IDXGIDevice
		if (SUCCEEDED(hr))
		{
			hr = m_pD3DDevice->QueryInterface(IID_PPV_ARGS(pDxgiDevice.GetAddressOf()));
		}
		// 获取Dxgi适配器 可以获取该适配器信息
		if (SUCCEEDED(hr))
		{
			hr = pDxgiDevice->GetAdapter(pDxgiAdapter.GetAddressOf());
		}
		// 获取Dxgi工厂
		if (SUCCEEDED(hr))
		{
			hr = pDxgiAdapter->GetParent(IID_PPV_ARGS(pDxgiFactory.GetAddressOf()));
		}
		// 创建交换链
		if (SUCCEEDED(hr))
		{
			hr = pDxgiFactory->CreateSwapChainForHwnd(
				m_pD3DDevice.Get(),
				(HWND)ui.DirectXWidget->winId(),
				&swapChainDesc,
				nullptr,
				nullptr,
				m_pSwapChain.GetAddressOf());
		}
		// 确保DXGI队列里边不会超过一帧
		if (SUCCEEDED(hr))
		{
			hr = pDxgiDevice->SetMaximumFrameLatency(1);
		}
	}

	// 设置屏幕方向
	if (SUCCEEDED(hr))
	{
		hr = m_pSwapChain->SetRotation(DXGI_MODE_ROTATION_IDENTITY);
	}
	// 利用交换链获取Dxgi表面
	if (SUCCEEDED(hr))
	{
		hr = m_pSwapChain->GetBuffer(0, IID_PPV_ARGS(pDxgiBackBuffer.GetAddressOf()));
	}
	// 利用Dxgi表面创建位图
	if (SUCCEEDED(hr))
	{
		D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(
			D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
			D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
			96.0f,
			96.0f
		);
		hr = m_pD2DDeviceContext->CreateBitmapFromDxgiSurface(
			pDxgiBackBuffer.Get(),
			&bitmapProperties,
			m_pD2DTargetBimtap.GetAddressOf());
	}
	// 设置
	if (SUCCEEDED(hr))
	{
		// 设置 Direct2D 渲染目标
		m_pD2DDeviceContext->SetTarget(m_pD2DTargetBimtap.Get());
	}

	pDxgiDevice.Reset();
	pDxgiAdapter.Reset();
	pDxgiFactory.Reset();
	pDxgiBackBuffer.Reset();
}

void ExMainHUD::resizeEvent(QResizeEvent* event)
{
	QWidget::resizeEvent(event);

	m_ClientWidth = event->size().width() - 200;
	m_ClientHeight = event->size().height() - 200;

	ui.DirectXWidget->setGeometry(QRect(100, 100, m_ClientWidth, m_ClientHeight));

	OnResize();
}

void ExMainHUD::closeEvent(QCloseEvent* event)
{
	QWidget::closeEvent(event);
	RenderThread->stop();
	RenderThread->quit();
	RenderThread->wait();
}

float ExMainHUD::AspectRatio() const
{
	return static_cast<float>(m_ClientWidth) / m_ClientHeight;
}

void ExMainHUD::UpdateRender()
{
	m_Timer.Tick();

	CalculateFrameStats();
	UpdateScene(m_Timer.DeltaTime());
	DrawScene();
}

void ExMainHUD::UpdateScene(float dt)
{


}

void ExMainHUD::DrawScene()
{
	assert(m_pD2DDeviceContext);

	// 开始渲染
	m_pD2DDeviceContext->BeginDraw();
	// 清屏
	m_pD2DDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::LightBlue));

	// 设置模糊程度，并显示图片
	float table[3] = { 0, 0.5f, m_afData / 500.f };
	m_pEffectTest->SetValue(D2D1_DISCRETETRANSFER_PROP_RED_TABLE, table);
	m_pEffectTest->SetValue(D2D1_DISCRETETRANSFER_PROP_GREEN_TABLE, table);
	m_pEffectTest->SetValue(D2D1_DISCRETETRANSFER_PROP_BLUE_TABLE, table);

	m_pD2DDeviceContext->DrawImage(m_pEffectTest.Get());

	m_pD2DDeviceContext->DrawText(
		L"Template project of Direct2D 1.1.",
		wcslen(L"Template project of Direct2D 1.1."),
		m_pTextFormat.Get(),
		D2D1::RectF(0, 0, 1080, 0),
		m_pBrush.Get());

	// 结束渲染
	m_pD2DDeviceContext->EndDraw();
	// 呈现目标
	m_pSwapChain->Present(0, 0);
}

bool ExMainHUD::ExNativeEvent(const QByteArray& eventType, void* message, long* result)
{
	//return 0;

	MSG* msg = (MSG*)message;

	//qDebug() << "msg[" << msg->message << "]  WM_MOUSEMOVE[" << WM_MOUSEMOVE << "]  WM_ACTIVATEAPP[" << WM_ACTIVATEAPP << "]";

	switch (msg->message)
	{
	case WM_INPUT:
	case WM_LBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_XBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_MBUTTONUP:
	case WM_RBUTTONUP:
	case WM_XBUTTONUP:
	case WM_MOUSEWHEEL:
	case WM_MOUSEHOVER:
	case WM_MOUSEMOVE:
		return true;
	case WM_ACTIVATEAPP:
		return true;
	case WM_SYSKEYUP:
		return true;
	}

	return false;
}

void ExMainHUD::CalculateFrameStats()
{
	// 该代码计算每秒帧速，并计算每一帧渲染需要的时间，显示在窗口标题
	static int frameCnt = 0;
	static float timeElapsed = 0.0f;

	//保存这一秒内有多少帧
	frameCnt++;

	if ((m_Timer.TotalTime() - timeElapsed) >= 1.0f)
	{
		float fps = (float)frameCnt; // fps = frameCnt / 1
		float mspf = 1000.0f / fps;

		QString windowsTitle = QString().sprintf("MainHUD FPS[%d] Frame Time[%fms] FrameCnt[%d]", (int)fps, mspf, frameCnt);
		setWindowTitle(windowsTitle);

		// Reset for next average.
		frameCnt = 0;
		timeElapsed += 1.0f;
	}
}

void ExMainHUD::mousePressEvent(QMouseEvent* event)
{
	QWidget::mousePressEvent(event);
}

void ExMainHUD::mouseReleaseEvent(QMouseEvent* event)
{
	QWidget::mouseReleaseEvent(event);
}

void ExMainHUD::wheelEvent(QWheelEvent* event)
{
	QWidget::wheelEvent(event);

	if (event->delta() > 0)
	{
		m_afData += 10;
	}
	else
	{
		m_afData -= 10;
		if (m_afData < 0)
		{
			m_afData = 0;
		}
	}
}

void ExMainHUD::keyPressEvent(QKeyEvent* event)
{
	QWidget::keyPressEvent(event);

	if (!event->isAutoRepeat())
	{
		keyStates.push_back(event->key());
	}
}

void ExMainHUD::keyReleaseEvent(QKeyEvent* event)
{
	QWidget::keyReleaseEvent(event);

	if (!event->isAutoRepeat())
	{
		keyStates.remove(event->key());
	}
}

bool ExMainHUD::isKeyDown(Qt::Key key)
{
	std::list<int>::iterator iter;
	for (iter = keyStates.begin(); iter != keyStates.end(); iter++)
	{
		if (*iter == static_cast<int>(key))
		{
			keyStates.remove(key);
			return true;
		}
	}
	return false;
}
